package com.ruoyix.flowable.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.ruoyix.common.core.domain.entity.SysUser;
import com.ruoyix.common.core.domain.model.LoginUser;
import com.ruoyix.common.helper.LoginHelper;
import com.ruoyix.common.core.page.TableDataInfo;
import com.ruoyix.common.core.domain.PageQuery;
import com.ruoyix.common.utils.StringUtils;
import com.ruoyix.flowable.domain.vo.*;
import com.ruoyix.flowable.enums.TaskStatus;
import com.ruoyix.flowable.service.IBpmTaskAssigneeService;
import com.ruoyix.flowable.service.IBpmTaskExtService;
import com.ruoyix.flowable.service.IBpmTaskService;
import com.ruoyix.system.mapper.SysUserMapper;
import lombok.RequiredArgsConstructor;

import org.flowable.engine.HistoryService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.history.HistoricProcessInstanceQuery;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.runtime.ProcessInstanceQuery;
import org.flowable.engine.task.Comment;
import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Service;
import com.ruoyix.flowable.domain.bo.BpmProcessInstanceBo;
import com.ruoyix.flowable.service.IBpmProcessInstanceService;
import org.flowable.engine.TaskService;
import org.springframework.transaction.annotation.Transactional;


import java.util.*;
import java.util.stream.Collectors;

/**
 * 流程实例Service业务层处理
 *
 * @author ruoyi
 * @date 2023-11-27
 */
@RequiredArgsConstructor
@Service
public class BpmProcessInstanceServiceImpl implements IBpmProcessInstanceService {

    private final SysUserMapper userMapper;

    private final IBpmTaskAssigneeService iBpmTaskAssigneeService;

    private final RuntimeService runtimeService;

    private  final TaskService taskService;

    private final HistoryService historyService;

    private final RepositoryService repositoryService;

    private final IBpmTaskExtService bpmTaskExtService;

    private final IBpmTaskService bpmTaskService;

    /**
     * 查询流程实例
     */
    @Override
    public Map<String, Object> queryById(String processInstanceId) {

        Map<String,Object> result = new HashMap<>();

        // 查询历史任务实例
        List<HistoricTaskInstance> historicTasks = historyService.createHistoricTaskInstanceQuery()
            .processInstanceId(processInstanceId)
            .orderByHistoricTaskInstanceStartTime()
            .asc()
            .list();

        String processDefinitionId = historicTasks.get(0).getProcessDefinitionId();
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(processDefinitionId).singleResult();

        //查询节点审批流程信息
        List<BpmTaskVo> bpmTaskVoList = getBpmTaskVoList(processInstanceId, historicTasks);
        result.put("bpmTaskVoList",bpmTaskVoList);

        //查询实例对应流程图
        String bpmnXml = StrUtil.utf8Str(IoUtil.readBytes( repositoryService.getProcessModel(processDefinition.getId()), false));
        result.put("bpmnXml",bpmnXml);


        //查询当前节点对应表单信息
        BpmTaskExtVo activeTaskForm = getActiveTaskForm(processInstanceId);
        result.put("activeTaskForm",activeTaskForm);

        //查询已完成节点对应表单信息
        List<BpmTaskExtVo> completeTaskList = getCompleteTaskForm(processInstanceId, historicTasks);
        result.put("completeTaskForm",completeTaskList);

        return result;
    }

    /**
     * 查询已完成节点对应表单信息
     * @param processInstanceId
     * @param historicTasks
     * @return
     */
    @NotNull
    private List<BpmTaskExtVo> getCompleteTaskForm(String processInstanceId, List<HistoricTaskInstance> historicTasks) {
        Task task = taskService.createTaskQuery()
            .processInstanceId(processInstanceId)
            .singleResult();
        List<BpmTaskExtVo> bpmTaskExtVoList = new ArrayList<>();
        //判断流程是否结束，是否还有激活的任务节点
        if(ObjectUtil.isNotEmpty(task)){
            List<HistoricTaskInstance> completeTaskList = historicTasks.stream().filter(item -> !item.getId().equals(task.getId())).collect(Collectors.toList());
            completeTaskList.forEach(item->{
                BpmTaskExtVo bpmTaskExtVo = bpmTaskExtService.queryById(item.getId());
                bpmTaskExtVoList.add(bpmTaskExtVo);
            });
        }else {
            historicTasks.forEach(item->{
                BpmTaskExtVo bpmTaskExtVo = bpmTaskExtService.queryById(item.getId());
                bpmTaskExtVoList.add(bpmTaskExtVo);
            });
        }

        return bpmTaskExtVoList;
    }

    /**
     * 获取执行中任务表单信息
     * @param processInstanceId
     * @return
     */
    @NotNull
    private BpmTaskExtVo getActiveTaskForm(String processInstanceId) {
        Task task = taskService.createTaskQuery()
            .processInstanceId(processInstanceId)
            .singleResult();
        BpmTaskExtVo bpmTaskExtVo = new BpmTaskExtVo();
        if(ObjectUtil.isNotEmpty(task)){
            BpmTaskAssigneeVo bpmTaskAssigneeVo = iBpmTaskAssigneeService.selectByDefineIdAndTaskKey(task.getProcessDefinitionId(), task.getTaskDefinitionKey());
            if(ObjectUtil.isNotEmpty(bpmTaskAssigneeVo)){
                bpmTaskExtVo.setId(task.getId());
                bpmTaskExtVo.setName(task.getName());
                bpmTaskExtVo.setFormType(bpmTaskAssigneeVo.getFormType());
                bpmTaskExtVo.setFormFields(bpmTaskAssigneeVo.getFormFields());
                bpmTaskExtVo.setFormOption(bpmTaskAssigneeVo.getFormOption());
                bpmTaskExtVo.setFormType(bpmTaskAssigneeVo.getFormType());
                bpmTaskExtVo.setFormCustomCreatePath(bpmTaskAssigneeVo.getFormCustomCreatePath());
            }else {
                throw new RuntimeException("流程定义信息未匹配");
            }
        }
        return bpmTaskExtVo;
    }

    /**
     * 查询节点审批流程信息
     * @param processInstanceId
     * @param historicTasks
     * @return
     */
    @NotNull
    private List<BpmTaskVo> getBpmTaskVoList(String processInstanceId, List<HistoricTaskInstance> historicTasks) {
        //查询任务节点信息
        List<BpmTaskVo> bpmTaskVoList = new ArrayList<>();
        for (HistoricTaskInstance historicTask : historicTasks) {
            BpmTaskVo  bpmTaskVo = new BpmTaskVo();
            bpmTaskVo.setStartTime(historicTask.getCreateTime());
            bpmTaskVo.setEndTime(historicTask.getEndTime());
            if(ObjectUtil.isNotEmpty(historicTask.getDurationInMillis())){
                bpmTaskVo.setUseTime(DateUtil.formatBetween(historicTask.getDurationInMillis()));
            }
            bpmTaskVo.setId(historicTask.getId());
            bpmTaskVo.setName(historicTask.getName());
            bpmTaskVo.setProcessDefinitionId(historicTask.getProcessDefinitionId());
            bpmTaskVo.setExecutionId(historicTask.getExecutionId());
            bpmTaskVo.setTaskDefinitionKey(historicTask.getTaskDefinitionKey());
            bpmTaskVo.setAssigneeId(historicTask.getAssignee());
            String assigneeId = historicTask.getAssignee();
            if (assigneeId != null) {
                SysUser sysUser = userMapper.selectUserById(Long.valueOf(historicTask.getAssignee()));
                bpmTaskVo.setAssignee(sysUser == null ? null : sysUser.getNickName());
            }
            // 获取任务的审批意见
            List<Comment> commentList = taskService.getProcessInstanceComments(processInstanceId);
            List<Comment> collect = commentList.stream().filter(item -> item.getTaskId().equals(historicTask.getId())).sorted(Comparator.comparing(Comment::getTime)).collect(Collectors.toList());
            bpmTaskVo.setCommentList(collect);



            bpmTaskVo.setTaskState(getTaskStatus(historicTask.getId(),bpmTaskVo));
            bpmTaskVoList.add(bpmTaskVo);
        }
        return bpmTaskVoList;
    }


    // 获取任务状态
    public String getTaskStatus(String taskId,BpmTaskVo bpmTaskVo) {
        HistoricTaskInstance historicTask = historyService.createHistoricTaskInstanceQuery()
            .taskId(taskId)
            .singleResult();

        if (historicTask != null && historicTask.getEndTime() != null) {
            List<Comment> processInstanceComments = taskService.getProcessInstanceComments(historicTask.getId());
            if(bpmTaskVo.getCommentList().get(bpmTaskVo.getCommentList().size()-1).getType().equals(TaskStatus.REJECTED.getTaskStatus())){
                return TaskStatus.REJECTED.getTaskStatus();
            }else {
                return TaskStatus.COMPLETED.getTaskStatus();
            }
        }

        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        if (task != null) {
            if (task.isSuspended()) {
                return TaskStatus.SUSPENDED.getTaskStatus();
            } else if (task.getDelegationState() != null) {
                return TaskStatus.DELEGATED.getTaskStatus();
            } else {
                return TaskStatus.ACTIVE.getTaskStatus();
            }
        } else {
            return TaskStatus.NOT_FOUND.getTaskStatus();
        }
    }



    /**
     * 查询流程实例列表
     */
    @Override
    public TableDataInfo<BpmProcessInstanceVo> queryPageList(BpmProcessInstanceBo bo, PageQuery pageQuery) {
        List<BpmProcessInstanceVo> result = new ArrayList<>();
        ProcessInstanceQuery processInstanceQuery = runtimeService.createProcessInstanceQuery()
            .variableValueEquals("initiatorId", LoginHelper.getUserId().toString());

        if (bo.getProcessDefinitionKey() != null) {
            processInstanceQuery.processDefinitionKey(bo.getProcessDefinitionKey());
        }

        List<ProcessInstance> processInstanceList = processInstanceQuery
            .orderByStartTime().desc()
            .listPage((pageQuery.getPageNum() - 1) * pageQuery.getPageSize(), pageQuery.getPageSize() * pageQuery.getPageNum());

        ProcessInstanceQuery countQuery = runtimeService.createProcessInstanceQuery()
            .variableValueEquals("initiatorId", LoginHelper.getUserId().toString());

        if (bo.getProcessDefinitionKey() != null) {
            countQuery.processDefinitionKey(bo.getProcessDefinitionKey());
        }

        long count = countQuery.count();
        processInstanceList.forEach(item->{
            BpmProcessInstanceVo processInstanceVo = new BpmProcessInstanceVo();
            processInstanceVo.setProcessInstanceId(item.getId());
            processInstanceVo.setProcessInstanceName(item.getProcessDefinitionName());
            processInstanceVo.setProcessDefinitionKey(item.getProcessDefinitionKey());
            processInstanceVo.setProcessDefinitionVersion(Long.valueOf(item.getProcessDefinitionVersion()));
            processInstanceVo.setStartTime(item.getStartTime());
            String initiatorId =(String) runtimeService.getVariable(item.getId(), "initiatorId");
            processInstanceVo.setStartUserId(initiatorId);
            SysUser sysUser = userMapper.selectUserById(Long.valueOf(initiatorId));
            if(ObjectUtil.isNotEmpty(sysUser)){
                processInstanceVo.setStartUserNickName(sysUser.getNickName());
            }

            Task currentTask = taskService.createTaskQuery()
                .processInstanceId(item.getId())
                .active()
                .singleResult();

            if (currentTask != null) {
                processInstanceVo.setCurrAssigneeId(currentTask.getAssignee());
                if(ObjectUtil.isNotEmpty(currentTask.getAssignee())){
                    SysUser assignee = userMapper.selectUserById(Long.valueOf(currentTask.getAssignee()));
                    processInstanceVo.setCurrAssignee(assignee == null ? null : assignee.getNickName());
                }
            }

            result.add(processInstanceVo);
        });
        TableDataInfo<BpmProcessInstanceVo> build = TableDataInfo.build(result);
        build.setTotal(count);
        return build;
    }


    /**
     * 获取已完成任务
     * @param bo
     * @param pageQuery
     * @return
     */
    @Override
    public TableDataInfo<BpmProcessInstanceVo> finishList(BpmProcessInstanceBo bo, PageQuery pageQuery) {
        List<BpmProcessInstanceVo> result = new ArrayList<>();
        HistoricProcessInstanceQuery historicProcessInstanceQuery = historyService.createHistoricProcessInstanceQuery()
            .variableValueEquals("initiatorId", LoginHelper.getUserId().toString())
            .finished();

        if (bo.getProcessDefinitionKey() != null) {
            historicProcessInstanceQuery.processDefinitionKey(bo.getProcessDefinitionKey());
        }

        List<HistoricProcessInstance> historicProcessInstanceList = historicProcessInstanceQuery.orderByProcessInstanceStartTime().desc()
            .listPage((pageQuery.getPageNum() - 1) * pageQuery.getPageSize(), pageQuery.getPageSize() * pageQuery.getPageNum());

        HistoricProcessInstanceQuery countQuery = historyService.createHistoricProcessInstanceQuery()
            .variableValueEquals("initiatorId", LoginHelper.getUserId().toString())
            .finished();

        if (bo.getProcessDefinitionKey() != null) {
            countQuery.processDefinitionKey(bo.getProcessDefinitionKey());
        }

        long count = countQuery.count();

        historicProcessInstanceList.forEach(item -> {
            BpmProcessInstanceVo processInstanceVo = new BpmProcessInstanceVo();
            processInstanceVo.setProcessInstanceId(item.getId());
            processInstanceVo.setProcessInstanceName(item.getProcessDefinitionName());
            processInstanceVo.setProcessDefinitionKey(item.getProcessDefinitionKey());
            processInstanceVo.setProcessDefinitionVersion(Long.valueOf(item.getProcessDefinitionVersion()));
            processInstanceVo.setStartTime(item.getStartTime());
            processInstanceVo.setEndTime(item.getEndTime());


            String initiatorId = (String) historyService.createHistoricVariableInstanceQuery()
                .processInstanceId(item.getId())
                .variableName("initiatorId")
                .singleResult()
                .getValue();
            processInstanceVo.setStartUserId(initiatorId);
            SysUser sysUser = userMapper.selectUserById(Long.valueOf(initiatorId));
            if (ObjectUtil.isNotEmpty(sysUser)) {
                processInstanceVo.setStartUserNickName(sysUser.getNickName());
            }

            // Get the last task for the process instance
            HistoricTaskInstance lastTask = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(item.getId())
                .orderByHistoricTaskInstanceEndTime().desc()
                .listPage(0, 1)
                .get(0);

            // If there is a last task, get the assignee and add it to the process instance vo
            if (lastTask != null) {
                processInstanceVo.setCurrAssigneeId(lastTask.getAssignee());
                if (ObjectUtil.isNotEmpty(lastTask.getAssignee())) {
                    SysUser assignee = userMapper.selectUserById(Long.valueOf(lastTask.getAssignee()));
                    processInstanceVo.setCurrAssignee(assignee == null ? null : assignee.getNickName());
                }
            }

            result.add(processInstanceVo);
        });

        TableDataInfo<BpmProcessInstanceVo> build = TableDataInfo.build(result);
        build.setTotal(count);
        return build;
    }





    /**
     * 新增流程实例
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BpmTaskApproveVo addProcessInstance(String processDefinitionId) {
        //todo 增加流程定义校验信息


        // 获取登录人信息
        LoginUser loginUser = LoginHelper.getLoginUser();

        // 启动流程实例
        ProcessInstance processInstance = runtimeService.startProcessInstanceById(processDefinitionId);

        // 设置流程变量，将登录人ID作为流程发起人
        runtimeService.setVariable(processInstance.getId(), "initiatorId", loginUser.getUserId().toString());

        // 获取第一个任务节点
        Task firstTask = taskService.createTaskQuery()
            .processInstanceId(processInstance.getId())
            .singleResult();

        //设置申请人节点审批人员
        taskService.setAssignee(firstTask.getId(),loginUser.getUserId().toString());

        //查询下一节点流程定义设置信息
        BpmTaskAssigneeVo bpmTaskAssigneeVo = iBpmTaskAssigneeService.selectByDefineIdAndTaskKey(processDefinitionId, firstTask.getTaskDefinitionKey());

        BpmTaskApproveVo bpmTaskApproveVo =  new BpmTaskApproveVo();
        bpmTaskApproveVo.setTaskId(firstTask.getId());
        bpmTaskApproveVo.setBpmTaskAssigneeVo(bpmTaskAssigneeVo);
        bpmTaskApproveVo.setProcessInstanceId(processInstance.getId());

        return bpmTaskApproveVo;
    }


    @Override
    public Boolean deleteHistoryProcessInstance(String processInstanceId) {
        historyService.deleteHistoricProcessInstance(processInstanceId);
        return true;

    }

    @Override
    public Boolean deleteProcessInstance(String processInstanceId) {
        runtimeService.deleteProcessInstance(processInstanceId,"delete");
        return true;
    }



    @Transactional(rollbackFor = Exception.class)
    @Override
    public String addBsProcessInstance(String processDefinitionKey,String formVariables) {
        //根据流程定义查询流程定义ID
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
            .processDefinitionKey(processDefinitionKey)
            .latestVersion()
            .singleResult();
        String processDefinitionId = processDefinition.getId();
        BpmTaskApproveVo bpmTaskApproveVo = addProcessInstance(processDefinitionId);

        // 获取登录人信息
        LoginUser loginUser = LoginHelper.getLoginUser();
        FlowDataVo flowDataVo = new FlowDataVo();
        flowDataVo.setAssignee(loginUser.getUserId().toString());
        flowDataVo.setComment("开始流程");
        bpmTaskApproveVo.setFlowData(flowDataVo);
        bpmTaskApproveVo.setFormCustomCreatePath(bpmTaskApproveVo.getBpmTaskAssigneeVo().getFormCustomCreatePath());
        bpmTaskApproveVo.setFormType(1L);
        bpmTaskApproveVo.setFormVariables(formVariables);

        //完成第一个节点任务
        bpmTaskService.complete(bpmTaskApproveVo);

        return bpmTaskApproveVo.getProcessInstanceId();
    }
}
